package net.cyndeline.rldungeon.dgs.strategy.pointlessArea.algorithmModules

import com.escalatesoft.subcut.inject.{Injectable, BindingModule}
import net.cyndeline.rldungeon.dgs.graph.{DGSFunctionality, Room}
import net.cyndeline.rldungeon.dgs.strategy.help.{SuperGraphVertexReplacer, SuperGraphVertexReplacerInterface, CollapsedNode, CollapsedEdge}
import scalax.collection.immutable.Graph
import scalax.collection.GraphEdge.UnDiEdge
import scalax.collection.GraphPredef.OuterEdge
import net.cyndeline.rlgraph.util.VertexReplacer
import net.cyndeline.rldungeon.dgs.EdgeCopyFactory
import net.cyndeline.rldungeon.dgs.strategy.pointlessArea.help.{RoomModifierInterface, RoomModifier, EdgesBelowNodes, NodeStorage}

/**
 * Used for dependency injection.
 */
trait AppendResultPostProcessingI {
  def setAsMeaningful[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType] with OuterEdge[X, EType]]
  (node: CollapsedNode[VType],
   collapsedGraph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner],
   regularGraph: Graph[VType, EType],
   edgeFactory: EdgeCopyFactory[VType, EType]): (Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner], Graph[VType, EType], CollapsedNode[VType])

  def unregisterEdge[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType] with OuterEdge[X, EType]]
  (edgeStorage: (NodeStorage[CollapsedNode[VType]], NodeStorage[CollapsedNode[VType]]),
   storage: Map[CollapsedNode[VType], NodeStorage[CollapsedNode[VType]]],
   areaConnections: Set[CollapsedNode[VType]],
   edgeMap: EdgesBelowNodes[VType]): Set[CollapsedNode[VType]]

  def replaceNodeInStorage[VType <: Room[VType]](oldN: CollapsedNode[VType],
                                                 newN: CollapsedNode[VType],
                                                 store: Map[CollapsedNode[VType], NodeStorage[CollapsedNode[VType]]]
                                                 ): Map[CollapsedNode[VType], NodeStorage[CollapsedNode[VType]]]
}

/**
 * Updates data structures after adding an activator/responder pair to a graph.
 */
class AppendResultPostProcessing(implicit val bindingModule: BindingModule)
  extends Injectable
  with AppendResultPostProcessingI {

  private val pointlessStatusSetter = injectOptional[RoomModifierInterface] getOrElse { new RoomModifier() }

  /* Replaces represented vertices inside a cycle-collapsed graph. */
  private val superNodeReplacer = injectOptional[SuperGraphVertexReplacerInterface] getOrElse { new SuperGraphVertexReplacer() }

  /**
   * Replaces every pointless vertex in a pointless collapsed node with meaningful copies. Also updates
   * the nodes collapsed graph with the new node, as well as replaces the old vertices with the new in
   * the regular graph.
   *
   * @param node Node with pointless vertices.
   * @param collapsedGraph Graph containing the pointless node.
   * @param regularGraph Graph containing the vertices the node represents.
   * @param edgeFactory Used when copying regular edges.
   * @tparam VType Type of vertex being represented by a collapsed node.
   * @tparam EType Type of edge being represented by collapsed edges.
   * @return A triple containing the updated collapsed graph, the updated regular graph and the updated node (present
   *         in the updated collapsed graph).
   */
  def setAsMeaningful[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType] with OuterEdge[X, EType]]
                     (node: CollapsedNode[VType],
                      collapsedGraph: Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner],
                      regularGraph: Graph[VType, EType],
                      edgeFactory: EdgeCopyFactory[VType, EType]): (Graph[CollapsedNode[VType], CollapsedEdge[VType]#Inner], Graph[VType, EType], CollapsedNode[VType]) = {
    if (!node.isPointless) {
      throw new Error("Cannot mark any vertices as meaningful, as no pointless vertices were found in vertex set " + node.allPointlessVertices.mkString(","))
    }

    val vertexReplacer = new VertexReplacer(edgeFactory)

    /* Set every pointless vertex in the updated node as meaningful. */
    val pointlessVertices = node.allPointlessVertices
    val meaningfulVertices = pointlessStatusSetter.setPointlessStatus(false, pointlessVertices)
    val replacement = superNodeReplacer.replaceVertexSetUsingNode(meaningfulVertices, node, collapsedGraph)
    val updatedCollapsedGraph = replacement._1
    val updatedNode = replacement._2

    /* Update the regular graph with the new vertices as well. */
    var currentGraph = regularGraph

    meaningfulVertices.foreach(kv => {
      currentGraph = vertexReplacer.replace(kv._1, kv._2, currentGraph)
    })

    (updatedCollapsedGraph, currentGraph, updatedNode)
  }

  /**
   * Removes an edge from an edge map and filters out any main area connections in it that no longer has edges below it.
   * @param edgeStorage Storage tuple representing an edge that has had a responder placed on it and no longer can carry
   *                    additional responders.
   * @param storage Maps collapsed nodes to their storage counterparts.
   * @param areaConnections All nodes along the main path in a collapsed graph that has edges below them.
   * @param edgeMap Maps edges below a node on the main path to the node itself. The mutable state of this map will be
   *                modified.
   * @tparam VType Type of vertex being represented by a collapsed node.
   * @tparam EType Type of edge being represented by collapsed edges.
   * @return A set of area connections with every connection that no longer has edges below it after the edge has been
   *         removed filtered out.
   */
  def unregisterEdge[VType <: Room[VType], EType[X] <: UnDiEdge[X] with DGSFunctionality[X, EType] with OuterEdge[X, EType]]
                    (edgeStorage: (NodeStorage[CollapsedNode[VType]], NodeStorage[CollapsedNode[VType]]),
                     storage: Map[CollapsedNode[VType], NodeStorage[CollapsedNode[VType]]],
                     areaConnections: Set[CollapsedNode[VType]],
                     edgeMap: EdgesBelowNodes[VType]): Set[CollapsedNode[VType]] = {
    edgeMap.removeEdges(Set(edgeStorage))
    areaConnections.filter(c => edgeMap.edgeAmountBelow(storage(c)) > 0)
  }

  /**
   * Replaces a node as key value in a storage map, and also updates it inside the storage.
   * @param oldN Old node already in the storage.
   * @param newN new node to replace the old node with,
   * @param store Storage mapping.
   * @tparam VType Type of vertex being represented by a collapsed node.
   * @return An updated storage mapping with the new node replacing the old.
   */
  def replaceNodeInStorage[VType <: Room[VType]](oldN: CollapsedNode[VType],
                                                 newN: CollapsedNode[VType],
                                                 store: Map[CollapsedNode[VType], NodeStorage[CollapsedNode[VType]]]
                                                 ): Map[CollapsedNode[VType], NodeStorage[CollapsedNode[VType]]] = {
    val storage = store(oldN)
    storage.node = newN
    store - oldN + (newN -> storage)
  }
}
